#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>

#ifdef X86_64
#warning X86_64
#else
#warning X86
#endif

#define packed_data __attribute__ ((__packed__))

// Fig. 3-8: Segment Descriptor, p. 1730
struct packed_data segment_descriptor_t
{
	// low word
	uint16_t segment_limit_0015;
	uint16_t base_address_0015;
	// high word
	uint16_t base_address_1623 : 7;
	uint8_t segment_type : 4;
	uint8_t descriptor_type : 1;
	uint8_t privilege_level : 2;
	uint8_t segment_present : 1;
	uint8_t segment_limit_1619 : 4;
	uint8_t avl : 1;
	uint8_t sixtyfour_code_segment : 1;
	uint8_t default_operation_size : 1;
	uint8_t granularity : 1;
	uint8_t base_2431 : 8;
};
typedef struct segment_descriptor_t segment_descriptor_t;

union packed_data segment_selector_t
{
	uint16_t selector;
	struct packed_data {
		uint8_t rpl : 2;
		uint8_t table : 1;
		uint16_t index : 13;
	};
};

struct packed_data segment_register_t
{
	// "hidden" part / "shadow register"
	segment_descriptor_t descriptor;
	// "visible" part
	union segment_selector_t selector;
};
typedef struct segment_register_t segment_register_t;

// Fig. 2-6: Memory Management Registers, p. 1708
#ifndef X86_64
#define systable_base_t uint32_t
#else   // when X86_64
#define systable_base_t uint64_t
#endif
struct packed_data system_table_register_t
{
	uint16_t limit;
	systable_base_t base;
};
typedef struct system_table_register_t system_table_register_t;

void read_ldt()
{
	/* ldt segment selector */
	union segment_selector_t ldtr_seg_sel = {};
	system_table_register_t gdtr = {0xcafe,
					(systable_base_t) 0xdeadcafef00dcafe};
	
	asm("sldt %0" :"=r"(ldtr_seg_sel));
	asm("sgdt %0" :"=m"(gdtr));

	printf("ldt segment selector = %d\n",
		ldtr_seg_sel.selector);

#ifndef X86_64
	printf("gdtr.limit = %x\ngdtr.base = %x\n",
#else   // when X86_64
        printf("gdtr.limit = %x\ngdtr.base = %llx\n",
#endif
		gdtr.limit, gdtr.base);	// %x"PRIu64"\n
	/*segment_descriptor_t* gdt = (segment_descriptor_t*)gdtr.base;
	for (uint16_t i = 0; i < gdtr.limit; ++i)
	{
		segment_descriptor_t entry = gdt[i];
		printf("segment_limit_0015 = %x\n",
			entry.segment_limit_0015);
	}*/
}

int main(int argc, char** argv)
{
	read_ldt();

	return 0;
}
